package br.unidocs.servico;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;

import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.NoResultException;
import javax.persistence.NonUniqueResultException;
import javax.persistence.PersistenceContext;

import org.apache.log4j.Logger;
import org.eclipse.persistence.exceptions.DatabaseException;

import br.unidocs.exceptions.BancoException;
import br.unidocs.exceptions.UsuarioInvalidoException;
import br.unidocs.modelo.entidade.Usuario;
import br.unidocs.util.MailSender;

/**
 * Session Bean implementation class UsuarioServicesImpl
 */
@Stateless
// nao mantem estado entre chamadas; o metodo processa e te devolve algo. as
// informacoes gravadas dentro da classe serao perdidas;
// tem Statefull, que mantem na memoria. e o terceiro é Nested tipo de servicos
// - consumir mensagens;
public class UsuarioEJBImpl implements UsuarioEJB {

	@PersistenceContext(unitName = "UnidocsPU")
	private EntityManager em; // inversao de controle , note a annotation. assim
								// o servidor injeta gerenciador de entidades

	private final Logger log = Logger.getLogger(this.getClass().getName());

	/**
	 * Default constructor.
	 */
	public UsuarioEJBImpl() {
		//
	}

	@SuppressWarnings("unchecked")
	@Override
	public List<Usuario> listaUsuarios() {
		return em.createQuery("select u from Usuario u order by u.id asc")
				.getResultList(); // u, é idem * aqui;
	}

	@Override
	// medodo criado porque estamos sobrescrevendo o metodo da interface
	public Usuario localizaUsuario(int id) {
		log.info("Localizando usuário com id " + id);
		Usuario u = em.find(Usuario.class, id);
		if (u != null) {
			log.debug("Usuário " + u.getNome() + " localizado.");
			em.refresh(u);// para nao pegar da memoria, forcar buscar no banco.
			return u;
		}
		log.debug("Usuário não localizado.");
		return null;
	}

	// @Override
	// public Usuario obterPorId(Integer id) {
	// return em.find(Usuario.class, id);
	// }

	@Override
	public void criaUsuario(Usuario novoUsuario) {
		em.persist(novoUsuario); // o jpa se vira pra localizar campos etc
	}

	@Override
	public void atualizaUsuario(Usuario usuario) {
		em.merge(usuario);
	}

	// @Override
	// public void atualizaCadastro(Usuario usuario) {
	// em.merge(usuario);
	// }

	@Override
	public void desativaUsuario(int id) {
		Usuario u = localizaUsuario(id);
		em.remove(u);
	}

	@Override
	public void excluiUsuario(Usuario u) {
		// 1 - apaga registro no banco de dados
		try {
			if (!em.contains(u)) {
				u = em.find(Usuario.class, u.getId());
			}

			em.remove(u);
		} catch (RuntimeException e) {
			if (e instanceof DatabaseException) {
				throw new BancoException("Não foi possível excluir o usuário.",
						e);
			} else
				throw e;
			// e.printStackTrace();
		}
	}

	@Override
	public Usuario validaUsuario(String login, String senha)
			throws UsuarioInvalidoException {
		try {
			Usuario u = (Usuario) em
					.createQuery(
					// eh jpql... similar a sql. aqui é tudo
					// referenciando atributos nas classes, nao os nomes no
					// banco;
					// --------Logo abaixo exemplo de validacao de login
					// sem e com criptografia---------------------------
							"select u from Usuario u where UPPER(u.login) = UPPER(:login) and u.senha = :senha")
					// "select u from Usuario u where UPPER(u.login) = UPPER(:login) and u.senha = crypt(':senha', u.senha)")

					.setParameter("login", login).setParameter("senha", senha)
					.getSingleResult();
			if (u.getDataDesativacao() != null
					&& u.getDataDesativacao().before(
							Calendar.getInstance().getTime())) {
				throw new UsuarioInvalidoException();
			}
			return u;
		} catch (NonUniqueResultException e) {
			log.info("Falha ao validar o login " + login);
			throw new UsuarioInvalidoException();
		} catch (NoResultException e) {
			log.info("Falha ao validar o login " + login);
			throw new UsuarioInvalidoException();
		}
	}

	@Override
	public void recuperaSenha(String email) throws UsuarioInvalidoException {
		// Localiza o usuario a partir do email
		Usuario u;
		try {
			u = (Usuario) em.createNamedQuery("localizaUsuarioPorEmail")
					.setParameter("email", email).getSingleResult();
		} catch (NonUniqueResultException e) {
			throw new UsuarioInvalidoException();
		} catch (NoResultException e) {
			throw new UsuarioInvalidoException();
		}

		// Encaminha a senha para o email
		MailSender sender = new MailSender();
		List<String> dest = new ArrayList<String>();
		dest.add(u.getEmail());
		sender.sendMail("sistemas.unifil@gmail.com", dest,
				"[Unidocs] Recuperação de senha",
				"Sua senha é: " + u.getSenha());
	}

	@Override
	public Usuario localizaPorEmail(String email)
			throws UsuarioInvalidoException {
		Usuario u;
		try {
			u = (Usuario) em.createNamedQuery("localizaUsuarioPorEmail")
					.setParameter("email", email).getSingleResult();
		} catch (NonUniqueResultException e) {
			throw new UsuarioInvalidoException();
		} catch (NoResultException e) {
			return null;
		}
		return u;
	}

	@Override
	public Usuario localizaPorLogin(String login)
			throws UsuarioInvalidoException {
		Usuario u;
		try {
			u = (Usuario) em.createNamedQuery("localizaUsuarioPorLogin")
					.setParameter("login", login).getSingleResult();
		} catch (NonUniqueResultException e) {
			throw new UsuarioInvalidoException();
		} catch (NoResultException e) {
			return null;
		}
		return u;
	}
}
